﻿using AdvVerifyModels.Clients;
using AdvVerifyModels.Models.Requests.SubstrateTracking;
using Refit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace AdvVerifyModels.SubstrateTracking
{
    public class WaferTransferClient(IWaferServiceClient client,
SubstrateTrackingEventSource source)
    {
        public async Task OpenFoup(int foup)
        {
            var resp = await client.OpenFoup(foup).ConfigureAwait(false);
            using var sub = await source.Subscribe().ConfigureAwait(false);
            for (; ; )
            {
                var e = await sub.Read().ConfigureAwait(false);
                if (e.TrackingId != resp.Id)
                {
                    continue;
                }
                switch (e)
                {
                    case FoupErrorEvent:
                        throw new Exception($"Failed to open foup{foup}");
                    case FoupOpenEvent:
                        return;
                }
            }
        }

        public async Task CloseFoup(int foup)
        {
            var resp = await client.CloseFoup(foup).ConfigureAwait(false);
            using var sub = await source.Subscribe().ConfigureAwait(false);
            for (; ; )
            {
                var e = await sub.Read().ConfigureAwait(false);
                if (e.TrackingId != resp.Id)
                {
                    continue;
                }
                switch (e)
                {
                    case FoupErrorEvent:
                        throw new Exception($"Failed to close foup{foup}");
                    case FoupClosedEvent:
                        return;
                }
            }
        }

        public async Task<WaferLoadingQueueClient> LoadAll(WaferLoadingQueueRequest req)
        {
            var resp = await client.LoadAll(req).ConfigureAwait(false);
            var sub = await source.Subscribe();
            return new WaferLoadingQueueClient(resp.Id, client, sub);
        }
    }

    public sealed class WaferLoadingQueueClient : IDisposable
    {
        readonly Guid id;
        readonly IWaferServiceClient client;
        readonly SubstrateTrackingSubscription eventSub;

        readonly CancellationTokenSource cts = new();

        public WaferLoadingQueueClient(Guid id,
            IWaferServiceClient client,
            SubstrateTrackingSubscription eventSub)
        {
            this.id = id;
            this.client = client;
            this.eventSub = eventSub;

            _ = Task.Run(async () =>
            {
                for (; ; )
                {
                    try
                    {
                        await Task.Delay(TimeSpan.FromSeconds(5), cts.Token).ConfigureAwait(false);
                        await client.QueueHealthCheck(id, cts.Token).ConfigureAwait(false);
                    }
                    catch (ApiException ex)
                    {
                        if (ex.StatusCode == HttpStatusCode.NotFound)
                        {
                            cts.Cancel();
                            return;
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        cts.Cancel();
                        return;
                    }
                    catch (Exception)
                    {
                        // ignored
                    }
                }
            });
        }

        public async Task Load()
        {
            for (; ; )
            {
                var e = await eventSub.Read(cts.Token);
                switch (e)
                {
                    case WaferTransferErrorEvent:
                        throw new Exception("Wafer transfer error");
                    case WaferTransferEndEvent:
                        throw new Exception("Wafer transfer canceled");
                    case WaferStateChangedEvent state:
                        if (state.State == WaferState.Processing)
                        {
                            return;
                        }
                        break;
                }
            }
        }

        public async Task AwaitCompletion()
        {
            for (; ; )
            {
                try
                {
                    var e = await eventSub.Read(cts.Token).ConfigureAwait(false);
                    switch (e)
                    {
                        case WaferTransferErrorEvent:
                            throw new Exception("Wafer transfer error");
                        case WaferTransferEndEvent:
                            return;
                    }
                }
                catch (OperationCanceledException)
                {
                    return;
                }
            }
        }

        public async Task Unload()
        {
            await client.QueueUnload(id).ConfigureAwait(false);
        }

        public async Task RequestCompletion()
        {
            await client.QueueRequestCompletion(id).ConfigureAwait(false);
        }

        public async Task Cancel()
        {
            await client.QueueCancel(id).ConfigureAwait(false);
        }

        public void Dispose()
        {
            cts.Cancel();
            eventSub.Dispose();
        }
    }
}
